#include "Scuba.h"
#include "Scope.h"
#include "Tokenizer.h"

#include "inGlobal.h"

#include<string>
#include<iostream>

using namespace std;

Scuba::Scuba(std::string in_sFileName)
{
    Scope *pxScope = new InGlobal(NULL);
    m_apxScope.push_back(pxScope);
    m_pxCurrentScope=pxScope;
	m_pxScubserver = NULL;

	setFileName(in_sFileName);

	m_pxSymbolyzer = new Symbolyzer();
	m_pxSymbolyzer->setFocus(m_pxCurrentScope, &m_xLocation);

	m_eStatus = eScubaDoneNone;
}

Scuba::~Scuba(void)
{
}

void Scuba::enterScope(Scope* inScope)
{
    Scope* pxOldScope = m_pxCurrentScope;

    m_apxScope.push_back(inScope);
    m_pxCurrentScope->m_axBody.push_back(Token("")); //Please note that the position of a new scope is denoted by a '\0'
    m_pxCurrentScope = inScope;

	m_pxSymbolyzer->onScopeEntry(*m_pxCurrentScope, *pxOldScope);

    //Notify the observer
    if(NULL!=m_pxScubserver)
        m_pxScubserver->onScopeEntry(*m_pxCurrentScope, *pxOldScope);

}

void Scuba::exitScope(void)
{
    Scope* pxOldScope;

    pxOldScope = m_apxScope.back();
    m_apxScope.pop_back();
    m_pxCurrentScope = m_apxScope.back();

	m_pxSymbolyzer->onScopeExit (*m_pxCurrentScope, *pxOldScope);

    if(NULL!=m_pxScubserver)
        m_pxScubserver->onScopeExit(*m_pxCurrentScope, *pxOldScope);
}

void Scuba::feed(Token& inToken)
{
    bool bIsEndOfScope=false;
    Scope* pxScope = NULL;

    char cDelimiter=char(inToken);
	switch(cDelimiter)
	{
	case '\n':
		m_xLocation.m_nLineNumber++;
		break;
	}

	m_pxSymbolyzer->onNewToken(*m_pxCurrentScope, inToken);

	if(NULL!=m_pxScubserver)
		m_pxScubserver->onNewToken(*m_pxCurrentScope, inToken);
	//TODO? It might be safer to inform Scubservers *AFTER* parse. Doubtful, but noting anyway

    pxScope = m_pxCurrentScope->parse(inToken, bIsEndOfScope);
    if(true==bIsEndOfScope)
        exitScope();
    
    if(NULL!=pxScope)
        enterScope(pxScope);

}

void Scuba::print()
{
    this->m_pxCurrentScope->printSymbols();
}

void Scuba::setObserver(Scubserver* inScubserver)
{
    m_pxScubserver = inScubserver;
	inScubserver->setFocus(m_pxCurrentScope, &m_xLocation);
}


void Scuba::dive(std::string in_sFileName, bool bReadFile, bool bTokenize, bool bParse)
{
	if(!bTokenize)	bParse	  = false;	//Cannot parse if there are no tokens

	setFileName(in_sFileName);

	if(bReadFile)	m_xReader.gobble(m_sFileName);
	if(bTokenize)	m_xTokenizer.tokenize(m_xReader.getStream());
	if(!bParse)
		return;

    int n = (int)m_xTokenizer.m_asTokens.size();
    for(int i = 0; i<n; i++)
    {
        feed(m_xTokenizer.m_asTokens[i]);
    }
	
	m_eStatus = eScubaDoneParse;
}

void Scuba::walk(Scubuddy& in_rxScubuddy)
{
	if(m_eStatus!= eScubaDoneParse)
		return;

	m_xLocation.m_nLineNumber = 1;
	in_rxScubuddy.setLocation(m_pxCurrentScope, &m_xLocation);
	walkScope(in_rxScubuddy, m_pxCurrentScope);
}

int Scuba::getNumberOfLines()
{
	return m_xReader.m_nLines;
}

void Scuba::walkScope(Scubuddy& in_rxScubuddy, Scope* in_pxScope)
{
	int i=0, nChildIndex=0;
	int n=in_pxScope->m_axBody.size();
	Token xToken;

	m_pxCurrentScope = in_pxScope;

	for(i=0; i<n; i++)
	{
		xToken = in_pxScope->m_axBody[i];

		if(xToken.m_sValue == "")
			//"" is the new-scope marker
		{
			Scope* pxNewScope = in_pxScope->m_apxChild[nChildIndex++];
			in_rxScubuddy.offScopeEntry(*pxNewScope, *in_pxScope);
			walkScope(in_rxScubuddy, pxNewScope);
			
			m_pxCurrentScope = in_pxScope;
			in_rxScubuddy.offScopeExit(*in_pxScope, *pxNewScope);
		}
		else if(xToken.m_sValue =="\n")
		{
			m_xLocation.m_nLineNumber++;
		}

		in_rxScubuddy.offNewToken(*in_pxScope, xToken);
	}
}

void Scuba::setFileName(std::string in_sFileName, int in_nLineNumber)
{
	m_sFileName = in_sFileName;

	m_xLocation.m_sFileName =  m_sFileName;
	m_xLocation.m_nLineNumber = in_nLineNumber;
}
